<?php
/*
 * 地图坐标系转换
 * */
namespace app\common;



class Map
{
    private static float $x_pi  = 52.35987755982988;
    private static float $pi  = 3.1415926535897932384626;
    private static float $a = 6378245.0;
    private static float $ee = 0.00669342162296594323;

    /**
     *
     * GCJ02坐标---->百度地图BD09坐标
     *
     * @param float $lat 纬度
     * @param float $lng 经度
     * @return array
     *
     */
    public static function GcToBd(float $lng,float $lat):array
    {
        $x_pi = self::$x_pi;
        $x = $lng;
        $y = $lat;
        $z = sqrt($x * $x + $y * $y) + 0.00002 * sin($y * $x_pi);
        $theta = atan2($y, $x) + 0.000003 * cos($x * $x_pi);
        $lng = $z * cos($theta) + 0.0065;
        $lat = $z * sin($theta) + 0.006;
        return ['lng'=>$lng,'lat'=>$lat];
    }


    /**
     *
     * 百度地图BD09坐标---->GCJ02坐标
     *
     * @param float $lat 纬度
     * @param float $lng 经度
     * @return array
     *
     */
    public static function BdToGc(float $lng,float $lat):array
    {
        $x_pi = self::$x_pi;
        $x = $lng - 0.0065;
        $y = $lat - 0.006;
        $z = sqrt($x * $x + $y * $y) - 0.00002 * sin($y * $x_pi);
        $theta = atan2($y, $x) - 0.000003 * cos($x * $x_pi);
        $lng = $z * cos($theta);
        $lat = $z * sin($theta);
        return ['lng'=>$lng,'lat'=>$lat];
    }


    /**
     *
     * BD09转WGS84
     *
     * @param float $lng
     * @param float $lat
     * @return array
     *
     * */
    public static function BdToWg(float $lng,float $lat):array
    {
        $gc = self::BdToGc($lng,$lat);
        $wg = self::GcToWg($gc['lng'],$gc['lat']);
        return ['lng' => $wg['lng'],'lat' => $wg['lat']];
    }

    /**
     *
     * WGS84转BD09
     *
     * @param float $lng
     * @param float $lat
     * @return array
     *
     * */
    public static function WgToBd(float $lng,float $lat):array
    {
        $gc = self::WgToGc($lng,$lat);
        $bd = self::GcToBd($gc['lng'],$gc['lat']);
        return ['lng' => $bd['lng'],'lat' => $bd['lat']];
    }


    /**
     *
     * WGS84转GCj02
     *
     * @param float $lng
     * @param float $lat
     * @return array
     *
     */
    public static function WgToGc(float $lng,float $lat):array
    {
        if (self::outOfChina($lng, $lat)) {
            return ['lng'=>$lng, 'lat'=>$lat];
        } else {
            $dlat = self::transformLat($lng - 105.0, $lat - 35.0);
            $dlng = self::transformLng($lng - 105.0, $lat - 35.0);
            $radlat = $lat / 180.0 * self::$pi;
            $magic = sin($radlat);
            $magic = 1 - self::$ee * $magic * $magic;
            $sqrtmagic = sqrt($magic);
            $dlat = ($dlat * 180.0) / ((self::$a * (1 - self::$ee)) / ($magic * $sqrtmagic) * self::$pi);
            $dlng = ($dlng * 180.0) / (self::$a / $sqrtmagic * cos($radlat) * self::$pi);
            $mglat = $lat + $dlat;
            $mglng = $lng + $dlng;
            return ['lng'=>$mglng, 'lat'=>$mglat];
        }
    }
    /**
     * GCJ02 转换为 WGS84
     *
     * @param float $lng
     * @param float $lat
     * @return array
     *
     */
    public static function GcToWg(float $lng,float $lat):array
    {
        if (self::outOfChina($lng, $lat)) {
            return ['lng'=>$lng, 'lat'=>$lat];
        } else {
            $dlat = self::transformLat($lng - 105.0, $lat - 35.0);
            $dlng = self::transformLng($lng - 105.0, $lat - 35.0);
            $radlat = $lat / 180.0 * self::$pi;
            $magic = sin($radlat);
            $magic = 1 - self::$ee * $magic * $magic;
            $sqrtmagic = sqrt($magic);
            $dlat = ($dlat * 180.0) / ((self::$a * (1 - self::$ee)) / ($magic * $sqrtmagic) * self::$pi);
            $dlng = ($dlng * 180.0) / (self::$a / $sqrtmagic * cos($radlat) * self::$pi);
            $mglat = $lat + $dlat;
            $mglng = $lng + $dlng;
            return ['lng'=>$lng * 2 - $mglng, 'lat'=>$lat * 2 - $mglat];
        }
    }

    /**
     * 转换维度
     * @param float $lat
     * @param float $lng
     * @return float
     * */
    private static function transformLat(float $lng,float $lat):float
    {
        $ret = -100.0 + 2.0 * $lng + 3.0 * $lat + 0.2 * $lat * $lat + 0.1 * $lng * $lat + 0.2 * sqrt(abs($lng));
        $ret += (20.0 * sin(6.0 * $lng * self::$pi) + 20.0 * sin(2.0 * $lng * self::$pi)) * 2.0 / 3.0;
        $ret += (20.0 * sin($lat * self::$pi) + 40.0 * sin($lat / 3.0 * self::$pi)) * 2.0 / 3.0;
        $ret += (160.0 * sin($lat / 12.0 * self::$pi) + 320 * sin($lat * self::$pi / 30.0)) * 2.0 / 3.0;
        return $ret;
    }

    /**
     * 转换经度
     * @param float $lat
     * @param float $lng
     * @return float
     * */
    private static function transformLng(float $lng,float $lat):float
    {
        $ret = 300.0 + $lng + 2.0 * $lat + 0.1 * $lng * $lng + 0.1 * $lng * $lat + 0.1 * sqrt(abs($lng));
        $ret += (20.0 * sin(6.0 * $lng * self::$pi) + 20.0 * sin(2.0 * $lng * self::$pi)) * 2.0 / 3.0;
        $ret += (20.0 * sin($lng * self::$pi) + 40.0 * sin($lng / 3.0 * self::$pi)) * 2.0 / 3.0;
        $ret += (150.0 * sin($lng / 12.0 * self::$pi) + 300.0 * sin($lng / 30.0 * self::$pi)) * 2.0 / 3.0;
        return $ret;
    }


    /**
     * 判断是否在国内，不在国内则不做偏移
     * @param float $lng
     * @param float $lat
     * @return bool
     */
    private static function outOfChina(float $lng,float $lat):bool
    {
        return ($lng < 72.004 || $lng > 137.8347) || ($lat < 0.8293 || $lat > 55.8271) ;
    }

    /**
     * 电子围栏算法，判断一个坐标，是否在围栏里面
     *
     * @param array $fences 电子围栏，是一组坐标数组 如：[['lng'=>117.31312,'lat'=>31.812486],['lng'=>117.317468,'lat'=>31.813867],['lng'=>117.320253,'lat'=>31.807546]]
     * @param float $lng 要判断的坐标经度,如：117.315779
     * @param float $lat 要判断的坐标纬度,如：31.808543
     * @return bool
     */
    public static function inFences(array $fences,float $lng,float $lat):bool
    {
        //结果
        $c = false;
        //围栏路径节点数
        $n = count($fences);
        if($n < 3){
            return $c;
        }
        //纬度数组
        $latS = [];
        //经度数组
        $lngS = [];
        //拆分围栏
        foreach ($fences as $r) {
            $latS[] = $r['lat'] ?? 0;
            $lngS[] = $r['lng'] ?? 0;
        }

        //计算
        for ($i = 0, $j = $n - 1; $i < $n; $j = $i++) {
            if (( ($lngS[$i] > $lng) != ($lngS[$j] > $lng) ) &&
                ($lat < ($latS[$j] - $latS[$i]) * ($lng - $lngS[$i]) / ($lngS[$j] - $lngS[$i]) + $latS[$i]))
                $c = !$c;
        }

        //返回结果
        return $c;
    }

    /**
     * 计算两点之间的距离
     * @param float $lng1 经度1
     * @param float $lat1 纬度1
     * @param float $lng2 经度2
     * @param float $lat2 纬度2
     * @param string $unit m，km
     * @param int $decimal 小数点精确位数
     * @return float
     */
    public static function distance(float $lng1,float $lat1,float $lng2,float $lat2,string $unit = 'km',int $decimal = 2):float
    {
        $unit = strtolower($unit);
        $units = ['m','km'];
        if(!in_array($unit,$units)){
            $unit = 'km';
        }
        $EARTH_RADIUS = 6370.996; // 地球半径系数
        $PI           = pi();

        $radLat1 = $lat1 * $PI / 180;
        $radLat2 = $lat2 * $PI / 180;

        $radLng1 = $lng1 * $PI / 180;
        $radLng2 = $lng2 * $PI / 180;

        $a = $radLat1 - $radLat2;
        $b = $radLng1 - $radLng2;

        $distance = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
        $distance = $distance * $EARTH_RADIUS;

        if ($unit == "m") {
            $distance *= 1000;
        }

        return round($distance, $decimal);
    }

}

